package com.ming.job.xxljob.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.ming.common.Options;
import com.ming.common.SortBy;
import com.ming.common.beetl.util.Result;
import com.ming.common.beetl.util.StrUtil;
import com.ming.common.util.ClassFieldUtil;
import com.ming.common.util.ResourceUtil;
import com.ming.common.util.number.NumberUtil;
import com.ming.common.xxljob.annotation.PermissionLimit;
import com.ming.common.xxljob.biz.model.ReturnT;
import com.ming.common.xxljob.enums.ExecutorBlockStrategyEnum;
import com.ming.common.xxljob.glue.GlueTypeEnum;
import com.ming.common.xxljob.util.DateUtil;
import com.ming.job.core.cron.CronExpression;
import com.ming.job.core.route.ExecutorRouteStrategyEnum;
import com.ming.job.core.scheduler.MisfireStrategyEnum;
import com.ming.job.core.scheduler.ScheduleTypeEnum;
import com.ming.job.core.thread.JobScheduleHelper;
import com.ming.job.core.thread.JobTriggerPoolHelper;
import com.ming.job.core.trigger.TriggerTypeEnum;
import com.ming.job.core.util.I18nUtil;
import com.ming.job.xxljob.entity.XxlJobGroup;
import com.ming.job.xxljob.entity.XxlJobInfo;
import com.ming.job.xxljob.entity.XxlJobLog;
import com.ming.job.xxljob.entity.XxlJobLogGlue;
import com.ming.job.xxljob.vo.XxlJobInfoVo;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.SQLReady;
import org.beetl.sql.core.page.PageResult;
import org.beetl.sql.core.query.LambdaQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/xxljob/task")
public class IvyXxlJobInfoController {
    private static final Logger logger = LoggerFactory.getLogger(IvyXxlJobInfoController.class);
    @Resource
    private SQLManager sqlManager;

    @Autowired
    private ResourceLoader resourceLoader;

    @PostMapping("/option")
    @PermissionLimit(limit = false)
    public Result<?> option(@RequestBody Map<String,Object> map) {
        return Result.OK(ClassFieldUtil.getFieldAnnoDescribe(XxlJobInfo.class, map));
    }

    @PostMapping("/options")
    @PermissionLimit(limit = false)
    public Result<?> elOptions(@RequestBody(required = false) Map<String,Object> map) {
        LambdaQuery<XxlJobInfo> query = sqlManager.lambdaQuery(XxlJobInfo.class);
        if(CollUtil.isNotEmpty(map)){
            query.andEq(XxlJobInfo::getJobGroup, LambdaQuery.filterEmpty(map.getOrDefault("jobGroup",null)));
        }
        List<XxlJobInfo> list = query.select(XxlJobInfo::getId,XxlJobInfo::getJobDesc,XxlJobInfo::getExecutorHandler);
        return Result.OK(list);
    }

    @PostMapping("/page")
    @PermissionLimit(limit = false)
    public Result<PageResult<XxlJobInfo>> page(@RequestBody XxlJobInfoVo vo){
        LambdaQuery<XxlJobInfo> lambdaQuery = sqlManager.lambdaQuery(XxlJobInfo.class);
        lambdaQuery.andEq(XxlJobInfo::getId, LambdaQuery.filterEmpty(vo.getId()));
        lambdaQuery.andEq(XxlJobInfo::getJobGroup, LambdaQuery.filterEmpty(vo.getJobGroup()));
        lambdaQuery.andEq(XxlJobInfo::getTriggerStatus, LambdaQuery.filterEmpty(vo.getTriggerStatus()));
        lambdaQuery.andLike(XxlJobInfo::getJobDesc, LambdaQuery.filterLikeEmpty(vo.getJobDesc()));
        lambdaQuery.andLike(XxlJobInfo::getExecutorHandler, LambdaQuery.filterLikeEmpty(vo.getExecutorHandler()));
        lambdaQuery.andLike(XxlJobInfo::getAuthor, LambdaQuery.filterLikeEmpty(vo.getAuthor()));
        Options options = vo.getOptions();
        List<SortBy> sortBy = options.getSortBy();
        for (SortBy sort : sortBy) {
            if ("desc".equalsIgnoreCase(sort.getOrder())) {
                lambdaQuery.desc(StrUtil.camelToSnake(sort.getKey()));
            } else {
                lambdaQuery.asc(StrUtil.camelToSnake(sort.getKey()));
            }
        }
        PageResult<XxlJobInfo> page = lambdaQuery.page(options.getPage(), options.getItemsPerPage());
        return Result.OK(page);
    }

    @PostMapping("/add")
    @PermissionLimit(limit = false)
    public Result<?> add(@RequestBody XxlJobInfo item){
        LambdaQuery<XxlJobGroup> xxlJobGroupLambdaQuery = sqlManager.lambdaQuery(XxlJobGroup.class);
        xxlJobGroupLambdaQuery.andEq(XxlJobGroup::getId, item.getJobGroup());
        XxlJobGroup group = xxlJobGroupLambdaQuery.single();

        // valid base
        if (group == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_choose")+I18nUtil.getString("jobinfo_field_jobgroup")) );
        }
        if (item.getJobDesc()==null || item.getJobDesc().trim().length()==0) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
        }
        if (item.getAuthor()==null || item.getAuthor().trim().length()==0) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
        }

        // valid trigger
        ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(item.getScheduleType(), null);
        if (scheduleTypeEnum == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
        }
        if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
            if (item.getScheduleConf()==null || !CronExpression.isValidExpression(item.getScheduleConf())) {
                return Result.error(ReturnT.FAIL_CODE, "Cron"+I18nUtil.getString("system_unvalid"));
            }
        } else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE/* || scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
            if (item.getScheduleConf() == null) {
                return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")) );
            }
            try {
                int fixSecond = Integer.valueOf(item.getScheduleConf());
                if (fixSecond < 1) {
                    return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
                }
            } catch (Exception e) {
                return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
            }
        }

        // valid job
        if (GlueTypeEnum.match(item.getGlueType()) == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_unvalid")) );
        }
        if (GlueTypeEnum.BEAN==GlueTypeEnum.match(item.getGlueType()) && (item.getExecutorHandler()==null || item.getExecutorHandler().trim().length()==0) ) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+"JobHandler") );
        }
        // 》fix "\r" in shell
        if (GlueTypeEnum.GLUE_SHELL==GlueTypeEnum.match(item.getGlueType()) && item.getGlueSource()!=null) {
            item.setGlueSource(item.getGlueSource().replaceAll("\r", ""));
        }

        // valid advanced
        if (ExecutorRouteStrategyEnum.match(item.getExecutorRouteStrategy(), null) == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) );
        }
        if (MisfireStrategyEnum.match(item.getMisfireStrategy(), null) == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("misfire_strategy")+I18nUtil.getString("system_unvalid")) );
        }
        if (ExecutorBlockStrategyEnum.match(item.getExecutorBlockStrategy(), null) == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) );
        }

        // 》ChildJobId valid
        if (item.getChildJobId()!=null && item.getChildJobId().trim().length()>0) {
            String[] childJobIds = item.getChildJobId().split(",");
            for (String childJobIdItem: childJobIds) {
                if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && NumberUtil.isNumeric(childJobIdItem)) {
                    LambdaQuery<XxlJobInfo> xxlJobInfoLambdaQuery = sqlManager.lambdaQuery(XxlJobInfo.class);
                    xxlJobInfoLambdaQuery.andEq(XxlJobInfo::getId, Integer.parseInt(childJobIdItem));
                    XxlJobInfo childJobInfo = xxlJobInfoLambdaQuery.single();
                    if (childJobInfo==null) {
                        return Result.error(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
                    }
                } else {
                    return Result.error(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
                }
            }

            // join , avoid "xxx,,"
            String temp = "";
            for (String job : childJobIds) {
                temp += job + ",";
            }
            temp = temp.substring(0, temp.length()-1);

            item.setChildJobId(temp);
        }

        // add in db
        item.setAddTime(new Date());
        item.setUpdateTime(new Date());
        item.setGlueUpdatetime(new Date());
        if(item.getTriggerStatus() == null){
            item.setTriggerStatus(0);
        }

        LambdaQuery<XxlJobInfo> query = sqlManager.lambdaQuery(XxlJobInfo.class);
        int i = query.insert(item);
        return Result.OK(i);
    }

    @PostMapping("/update")
    @PermissionLimit(limit = false)
    public Result<?> update(@RequestBody XxlJobInfo jobInfo){
        // valid base
        if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
        }
        if (jobInfo.getAuthor()==null || jobInfo.getAuthor().trim().length()==0) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
        }

        // valid trigger
        ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
        if (scheduleTypeEnum == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
        }
        if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
            if (jobInfo.getScheduleConf()==null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {
                return Result.error(ReturnT.FAIL_CODE, "Cron"+I18nUtil.getString("system_unvalid") );
            }
        } else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE /*|| scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
            if (jobInfo.getScheduleConf() == null) {
                return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
            }
            try {
                int fixSecond = Integer.valueOf(jobInfo.getScheduleConf());
                if (fixSecond < 1) {
                    return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
                }
            } catch (Exception e) {
                return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
            }
        }

        // valid advanced
        if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) );
        }
        if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("misfire_strategy")+I18nUtil.getString("system_unvalid")) );
        }
        if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) );
        }

        // 》ChildJobId valid
        if (jobInfo.getChildJobId()!=null && jobInfo.getChildJobId().trim().length()>0) {
            String[] childJobIds = jobInfo.getChildJobId().split(",");
            for (String childJobIdItem: childJobIds) {
                if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && NumberUtil.isNumeric(childJobIdItem)) {
                    LambdaQuery<XxlJobInfo> xxlJobInfoLambdaQuery = sqlManager.lambdaQuery(XxlJobInfo.class);
                    xxlJobInfoLambdaQuery.andEq(XxlJobInfo::getId, Integer.parseInt(childJobIdItem));
                    XxlJobInfo childJobInfo = xxlJobInfoLambdaQuery.single();
                    if (childJobInfo==null) {
                        return Result.error(ReturnT.FAIL_CODE,MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
                    }
                } else {
                    return Result.error(ReturnT.FAIL_CODE,MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
                }
            }

            // join , avoid "xxx,,"
            String temp = "";
            for (String item:childJobIds) {
                temp += item + ",";
            }
            temp = temp.substring(0, temp.length()-1);

            jobInfo.setChildJobId(temp);
        }

        // group valid
        LambdaQuery<XxlJobGroup> xxlJobGroupLambdaQuery = sqlManager.lambdaQuery(XxlJobGroup.class);
        xxlJobGroupLambdaQuery.andEq(XxlJobGroup::getId, jobInfo.getJobGroup());
        XxlJobGroup group = xxlJobGroupLambdaQuery.single();
        if (group == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_jobgroup")+I18nUtil.getString("system_unvalid")) );
        }

        // stage job info
        XxlJobInfo exists_jobInfo = sqlManager.lambdaQuery(XxlJobInfo.class).andEq(XxlJobInfo::getId, jobInfo.getId()).single();
        if (exists_jobInfo == null) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_id")+I18nUtil.getString("system_not_found")) );
        }

        // next trigger time (5s后生效，避开预读周期)
        long nextTriggerTime = exists_jobInfo.getTriggerNextTime();
        boolean scheduleDataNotChanged = jobInfo.getScheduleType().equals(exists_jobInfo.getScheduleType()) && jobInfo.getScheduleConf().equals(exists_jobInfo.getScheduleConf());
        if (exists_jobInfo.getTriggerStatus() == 1 && !scheduleDataNotChanged) {
            try {
                com.ming.job.core.model.XxlJobInfo targetObj = new com.ming.job.core.model.XxlJobInfo();
                BeanUtil.copyProperties(jobInfo, targetObj);
                Date nextValidTime = JobScheduleHelper.generateNextValidTime(targetObj, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
                if (nextValidTime == null) {
                    return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
                }
                nextTriggerTime = nextValidTime.getTime();
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
            }
        }

        exists_jobInfo.setJobGroup(jobInfo.getJobGroup());
        exists_jobInfo.setJobDesc(jobInfo.getJobDesc());
        exists_jobInfo.setAuthor(jobInfo.getAuthor());
        exists_jobInfo.setAlarmEmail(jobInfo.getAlarmEmail());
        exists_jobInfo.setScheduleType(jobInfo.getScheduleType());
        exists_jobInfo.setScheduleConf(jobInfo.getScheduleConf());
        exists_jobInfo.setMisfireStrategy(jobInfo.getMisfireStrategy());
        exists_jobInfo.setExecutorRouteStrategy(jobInfo.getExecutorRouteStrategy());
        exists_jobInfo.setExecutorHandler(jobInfo.getExecutorHandler());
        exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
        exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
        exists_jobInfo.setExecutorTimeout(jobInfo.getExecutorTimeout());
        exists_jobInfo.setExecutorFailRetryCount(jobInfo.getExecutorFailRetryCount());
        exists_jobInfo.setChildJobId(jobInfo.getChildJobId());
        exists_jobInfo.setTriggerNextTime(nextTriggerTime);
        exists_jobInfo.setUpdateTime(new Date());
        exists_jobInfo.setGlueType(jobInfo.getGlueType());
        if(cn.hutool.core.util.StrUtil.isNotBlank(jobInfo.getChainId())){
            exists_jobInfo.setChainId(jobInfo.getChainId());
        }

        int i = sqlManager.updateTemplateById(exists_jobInfo);
        return Result.OK("更新成功", i);
    }

    @PostMapping("/delete")
    @PermissionLimit(limit = false)
    public Result<?> delete(@RequestBody XxlJobInfo item){
        Integer id = item.getId();
        XxlJobInfo xxlJobInfo = sqlManager.lambdaQuery(XxlJobInfo.class).andEq(XxlJobInfo::getId, id).single();
        if (xxlJobInfo == null) {
            return Result.OK();
        }
        int j = sqlManager.lambdaQuery(XxlJobLog.class).andEq(XxlJobLog::getJobId, id).delete();
        int k = sqlManager.lambdaQuery(XxlJobLogGlue.class).andEq(XxlJobLogGlue::getJobId, id).delete();
        int i = sqlManager.lambdaQuery(XxlJobInfo.class).andEq(XxlJobInfo::getId, id).delete();
        return Result.OK("删除成功",i);
    }

    //执行一次
    @PostMapping("/trigger")
    @PermissionLimit(limit = false)
    public Result<?> triggerJob(@RequestBody XxlJobInfoVo vo) {
        Integer id = vo.getId();
        String executorParam = vo.getExecutorParam();
        String addressList = vo.getAddressList();
        if (executorParam == null) {
            executorParam = "";
        }
        JobTriggerPoolHelper.trigger(id, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
        return Result.OK();
    }

    @PostMapping("/nextTriggerTime")
    @PermissionLimit(limit = false)
    public Result<?> nextTriggerTime(@RequestBody XxlJobInfo xxlJobInfo) {
        com.ming.job.core.model.XxlJobInfo paramXxlJobInfo = new com.ming.job.core.model.XxlJobInfo();
        paramXxlJobInfo.setScheduleType(xxlJobInfo.getScheduleType());
        paramXxlJobInfo.setScheduleConf(xxlJobInfo.getScheduleConf());

        List<String> result = new ArrayList<>();
        try {
            Date lastTime = new Date();
            for (int i = 0; i < 5; i++) {
                lastTime = JobScheduleHelper.generateNextValidTime(paramXxlJobInfo, lastTime);
                if (lastTime != null) {
                    result.add(DateUtil.formatDateTime(lastTime));
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) + e.getMessage());
        }
        return Result.OK(result);
    }

    @PostMapping("/start")
    @PermissionLimit(limit = false)
    public Result<?> start(@RequestBody XxlJobInfo item) {
        Integer id = item.getId();
        XxlJobInfo xxlJobInfo = sqlManager.lambdaQuery(XxlJobInfo.class).andEq(XxlJobInfo::getId, id).single();

        // valid
        ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(xxlJobInfo.getScheduleType(), ScheduleTypeEnum.NONE);
        if (ScheduleTypeEnum.NONE == scheduleTypeEnum) {
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type_none_limit_start")) );
        }

        // next trigger time (5s后生效，避开预读周期)
        long nextTriggerTime = 0;
        try {
            com.ming.job.core.model.XxlJobInfo targetObj = new com.ming.job.core.model.XxlJobInfo();
            BeanUtil.copyProperties(xxlJobInfo, targetObj);
            Date nextValidTime = JobScheduleHelper.generateNextValidTime(targetObj, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
            if (nextValidTime == null) {
                return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
            }
            nextTriggerTime = nextValidTime.getTime();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return Result.error(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );
        }

        xxlJobInfo.setTriggerStatus(1);
        xxlJobInfo.setTriggerLastTime(0);
        xxlJobInfo.setTriggerNextTime(nextTriggerTime);
        xxlJobInfo.setUpdateTime(new Date());
        int i = sqlManager.updateTemplateById(xxlJobInfo);
        if(i > 0){
            return Result.OK("任务开始成功！",i);
        }
        return Result.error("任务开始失败！");
    }

    @PostMapping("/stop")
    @PermissionLimit(limit = false)
    public Result<?> pause(@RequestBody XxlJobInfo xxlJobInfo) {
        XxlJobInfo jobInfo = sqlManager.lambdaQuery(XxlJobInfo.class).andEq(XxlJobInfo::getId, xxlJobInfo.getId()).single();
        jobInfo.setTriggerStatus(0);
        jobInfo.setTriggerLastTime(0);
        jobInfo.setTriggerNextTime(0);
        jobInfo.setUpdateTime(new Date());
        int i = sqlManager.updateTemplateById(jobInfo);
        if(i > 0){
            return Result.OK("任务停止成功！",i);
        }
        return Result.error("任务停止失败！");
    }

    @PostMapping("/code/refresh")
    @PermissionLimit(limit = false)
    public Result<?> codeRefresh(@RequestBody XxlJobInfo xxlJobInfo) {
        String glueType = xxlJobInfo.getGlueType();
        String root = "classpath:templates/codetemplate/";
        if("GLUE_GROOVY".equals(glueType)){
            xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_GROOVY.ftl"));
        }else if("GLUE_SHELL".equals(glueType)){
            xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_SHELL.ftl"));
        }else if("GLUE_PYTHON".equals(glueType)){
            xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_PYTHON.ftl"));
        }else if("GLUE_PHP".equals(glueType)){
            xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_PHP.ftl"));
        }else if("GLUE_NODEJS".equals(glueType)){
            xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_NODEJS.ftl"));
        }else if("GLUE_POWERSHELL".equals(glueType)){
            xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_POWERSHELL.ftl"));
        }
        return Result.OK(xxlJobInfo);
    }

    @PostMapping("/code/init")
    @PermissionLimit(limit = false)
    public Result<?> codeInit(@RequestBody XxlJobInfo xxlJobInfo) {
        if(StrUtil.isEmpty(xxlJobInfo.getGlueSource())){
            String glueType = xxlJobInfo.getGlueType();
            String root = "classpath:templates/codetemplate/";
            if("GLUE_GROOVY".equals(glueType)){
                xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_GROOVY.ftl"));
            }else if("GLUE_SHELL".equals(glueType)){
                xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_SHELL.ftl"));
            }else if("GLUE_PYTHON".equals(glueType)){
                xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_PYTHON.ftl"));
            }else if("GLUE_PHP".equals(glueType)){
                xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_PHP.ftl"));
            }else if("GLUE_NODEJS".equals(glueType)){
                xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_NODEJS.ftl"));
            }else if("GLUE_POWERSHELL".equals(glueType)){
                xxlJobInfo.setGlueSource(ResourceUtil.getResourceFtl(resourceLoader,root,"GLUE_POWERSHELL.ftl"));
            }
        }
        List<XxlJobLogGlue> glueList = sqlManager.lambdaQuery(XxlJobLogGlue.class).andEq(XxlJobLogGlue::getJobId, xxlJobInfo.getId()).select();
        xxlJobInfo.setXxlJobLogGlueList(glueList);
        return Result.OK(xxlJobInfo);
    }

    @PostMapping("/code")
    @PermissionLimit(limit = false)
    public Result<?> code(@RequestBody XxlJobInfo xxlJobInfo) {
        Integer id = xxlJobInfo.getId();
        String glueSource = xxlJobInfo.getGlueSource();
        String glueRemark = xxlJobInfo.getGlueRemark();
        // valid
        if (glueRemark==null) {
            return Result.error(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) );
        }
        if (glueRemark.length()<4 || glueRemark.length()>100) {
            return Result.error(500, I18nUtil.getString("jobinfo_glue_remark_limit"));
        }
        XxlJobInfo exists_jobInfo = sqlManager.lambdaQuery(XxlJobInfo.class).andEq(XxlJobInfo::getId, id).single();
        if (exists_jobInfo == null) {
            return Result.error(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
        }

        // update new code
        exists_jobInfo.setGlueSource(glueSource);
        exists_jobInfo.setGlueRemark(glueRemark);
        exists_jobInfo.setGlueUpdatetime(new Date());

        exists_jobInfo.setUpdateTime(new Date());

        sqlManager.updateTemplateById(XxlJobInfo.class,exists_jobInfo);

        // log old code
        XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue();
        xxlJobLogGlue.setJobId(exists_jobInfo.getId());
        xxlJobLogGlue.setGlueType(exists_jobInfo.getGlueType());
        xxlJobLogGlue.setGlueSource(glueSource);
        xxlJobLogGlue.setGlueRemark(glueRemark);

        xxlJobLogGlue.setAddTime(new Date());
        xxlJobLogGlue.setUpdateTime(new Date());

        sqlManager.insertTemplate(XxlJobLogGlue.class, xxlJobLogGlue);

        // remove code backup more than 30
        sqlManager.executeUpdate(new SQLReady("DELETE FROM xxl_job_logglue WHERE id NOT in( SELECT id FROM( SELECT id FROM xxl_job_logglue WHERE `job_id` = ? ORDER BY update_time desc LIMIT 0, ? ) t1 ) AND `job_id` = ?",exists_jobInfo.getId(), 30, exists_jobInfo.getId()));

        return Result.OK();
    }
}
